package com.irdstudio.efp.nls.service.impl.dxm;


import java.lang.reflect.Method;
import java.math.BigDecimal;

import com.irdstudio.basic.framework.core.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.irdstudio.basic.framework.core.exception.BizException;
import com.irdstudio.basic.framework.core.exception.HangException;
import com.irdstudio.basic.framework.core.util.DateUtility;
import com.irdstudio.basic.framework.core.util.TimeUtil;
import com.irdstudio.efp.basic.framework.mq.client.producer.MQProducerMessageClient;
import com.irdstudio.efp.esb.common.constant.EsbBizEnums;
import com.irdstudio.efp.esb.common.constant.SDicClientConstant;
import com.irdstudio.efp.esb.common.constant.dxm.DxmEnum;
import com.irdstudio.efp.esb.common.util.SDicMappingClient;
import com.irdstudio.efp.esb.service.bo.req.dxm.ReqLoanAdjBizmessage;
import com.irdstudio.efp.esb.service.bo.req.dxm.ReqLoanAdjLmtNoticeBean;
import com.irdstudio.efp.esb.service.bo.resp.dxm.RespLoanAdjLmtNoticeBean;
import com.irdstudio.efp.esb.service.facade.dxm.LoanAdjLmtNoticeService;
import com.irdstudio.efp.limit.service.facade.LmtAdjustThirdService;
import com.irdstudio.efp.limit.service.vo.LmtAdjustThirdVO;
import com.irdstudio.efp.nls.common.constant.PrdAdaptedSoltEnums;
import com.irdstudio.efp.nls.common.constant.QueueTaskEnums;
import com.irdstudio.efp.nls.service.facade.dxm.LimitSoltService;
import com.irdstudio.efp.nls.service.facade.queue.NlsProcessBizService;
import com.irdstudio.efp.nls.service.facade.queue.NlsQueueTaskService;
import com.irdstudio.efp.nls.service.vo.queue.NlsProcessBizVO;

/**
 * 额度调整队列插槽服务实现类
 * @author  qiuyf
 * @since 2019年3月12日 下午4:00:45
 * @version 1.0
 */
public abstract class LimitSoltServiceImpl implements LimitSoltService {

	private static Logger logger = LoggerFactory.getLogger(LimitSoltServiceImpl.class);

	/**
	 * 队列插槽服务实现方法
	 */
	@Override
	public void execute(NlsProcessBizVO nlsProcessBizVo, LmtAdjustThirdVO lmtAdjustThirdVo, int soltOrder) throws HangException, BizException, Exception {
		// 预处理
		String applySeq = nlsProcessBizVo.getApplySeq();
		String soltCode = nlsProcessBizVo.getSoltCode();
		String soltName = nlsProcessBizVo.getSoltName();
		String soltService = nlsProcessBizVo.getSoltService();
		logger.info("*********************************************");
		logger.info("*            开始一个新的插槽                 * ");
		logger.info("* 申请流水号 : 【" + applySeq + "】  *");
		logger.info("* 插槽代码 : 【" + soltCode + "】  *");
		logger.info("* 插槽名称 : 【" + soltName + "】  *");
		logger.info("* 插槽服务 : 【" + soltService + "】  *");
		logger.info("*********************************************");
		try {
			// 根据插槽代码映射
			Method methodSoltstate = nlsProcessBizVo.getClass().getMethod("setSoltState" + soltOrder, String.class);
			// 更新该插槽状态为执行中
			logger.info("申请流水号 : 【" + applySeq + "】  更新该插槽状态为执行中开始");
			methodSoltstate.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.EXECUTING.getCode());
			nlsProcessBizService.updateByPk(nlsProcessBizVo);
			logger.info("申请流水号 : 【" + applySeq + "】  更新该插槽状态为执行中结束");
			logger.info("申请流水号 : 【" + applySeq + "】  调用该插槽的业务逻辑处理开始");
			this.doExecute(nlsProcessBizVo, lmtAdjustThirdVo);
			logger.info("申请流水号 : 【" + applySeq + "】  调用该插槽的业务逻辑处理结束");
			boolean exceptionFlag = nlsProcessBizVo.isExceptionFlag();
			int hangTimes = nlsProcessBizVo.getHangTimes();
			logger.info("申请流水号 : 【" + applySeq + "】  根据异常标志【" + exceptionFlag + "】处理后续网贷任务队列信息");
			// 异常处理，执行以上服务完成后更新【 网贷业务过程 】中对应的插槽状态
			if (exceptionFlag) {
				Exception exception = nlsProcessBizVo.getExceptionMsg();
				logger.info("申请流水号 : 【" + applySeq + "】 根据异常【" + exception + "】处理后续网贷任务队列信息");
				
				/**
				 * 目前百度贷后调额业务不需要到挂起重跑
				 * @date 2019-04-11
				if (exception instanceof HangException) {
					logger.info("申请流水号 : 【" + applySeq + "】 处理该插槽的挂起异常开始");
					// 每次轮询队列任务表中挂起任务后再次触发挂起异常后，对挂起次数进行控制，如果大于等于三次直接失败
					hangTimes = hangTimes + 1;
					if (hangTimes > QueueTaskEnums.QueueHangTimesEnum.MAX.getTimes()) {
						logger.info("申请流水号 : 【" + applySeq + "】 异常类型为挂起异常时，该插槽挂起次数为：【" + hangTimes + "】，更新该插槽状态为失败，更新网贷任务队列中队列任务状态为执行失败， 更新第三方额度调整申请表中调整状态为拒绝开始");
						// 更新该插槽状态为失败
						methodSoltstate.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.FAILURE.getCode());
						nlsProcessBizVo.setHangTimes(hangTimes);
						nlsProcessBizVo.setQueueTaskState(QueueTaskEnums.QueueTaskStateEnum.FAILURE.getCode());
						*//** 设置插槽结果信息 **//*
						lmtAdjustThirdVo.setSoltResultDetails(JSONObject.toJSONString(nlsProcessBizVo));
						*//** 设置结果码 **//*
						lmtAdjustThirdVo.setRspCode(NlsProcessBizEnums.RspInfoEnum.HANG_MAX_TIMES.getRspCode());
						*//** 设置结果信息 **//*
						lmtAdjustThirdVo.setRspMsg(NlsProcessBizEnums.RspInfoEnum.HANG_MAX_TIMES.getRspMsg());
						*//** 设置调整状态信息为失败 **//*
						lmtAdjustThirdVo.setAdjStatus(DxmEnum.LoanAdjustSts.REJECT.getVALUE());
						logger.info("申请流水号 : 【" + applySeq + "】 异常类型为挂起异常时，该插槽挂起次数为：【" + hangTimes + "】，更新该插槽状态为失败，更新网贷任务队列中队列任务状态为执行失败， 更新第三方额度调整申请表中调整状态为拒绝结束");
						logger.info("申请流水号 : 【" + applySeq + "】执行 通知授信审批失败逻辑");
						// 通知调额失败
						noticeLoanAdjLmt(nlsProcessBizVo, lmtAdjustThirdVo, DxmEnum.RetCd.INTERFACE_INNER_DEAL_FAIL.getValue());
					} else {
						*//** 更新该插槽状态为挂起 **//*
						logger.info("申请流水号 : 【" + applySeq + "】异常类型为挂起异常时更新该插槽状态为挂起，更新网贷任务队列中队列任务状态为挂起，更新 申请进件信息中申请状态为挂起");
						methodSoltstate.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.HANG.getCode());
						nlsProcessBizVo.setHangTimes(hangTimes);
						nlsProcessBizVo.setQueueTaskState(QueueTaskEnums.QueueTaskStateEnum.HANG.getCode());
						*//** 设置插槽结果信息 **//*
						lmtAdjustThirdVo.setSoltResultDetails(JSONObject.toJSONString(nlsProcessBizVo));
						*//** 设置结果码 **//*
						lmtAdjustThirdVo.setRspCode(NlsProcessBizEnums.RspInfoEnum.HANG.getRspCode());
						*//** 设置结果信息 **//*
						lmtAdjustThirdVo.setRspMsg(NlsProcessBizEnums.RspInfoEnum.HANG.getRspMsg());
						*//** 设置调整状态信息为挂起 **//*
						lmtAdjustThirdVo.setAdjStatus(DxmEnum.LoanAdjustSts.HANG.getVALUE());
						*//** 设置结果信息 **//*
						lmtAdjustThirdVo.setRejectReason(exception.getMessage());
						*//** 最近修改时间 **//*
						nlsProcessBizVo.setLastModifyTime(DateUtility.getCurrAppDateTimeString());
						*//** 最近修改时间 **//*
						lmtAdjustThirdVo.setLastUpdateTime(DateUtility.getCurrAppDateTimeString());

						nlsProcessBizService.updateByPk(nlsProcessBizVo);
						lmtAdjustThirdService.updateByPk(lmtAdjustThirdVo);

						logger.info("申请流水号 : 【" + applySeq + "】处理该插槽的挂起异常结束");
						*//** 发送MQ挂起消息 add by wangyk date:20190222 **//*
						producerService.sendMsg(new Message(TopicEnums.QUEUE_TASK_HANG_LIMIT_TOPIC.getTopicId(), TagEnums.TAG_QUEUE_HANG_TASK.getTagId(), 
								lmtAdjustThirdVo.getAdjAmtId().concat(".").concat(String.valueOf(hangTimes)),
								JSON.toJSONString(lmtAdjustThirdVo).getBytes("UTF-8")),1);
						throw exception;
					}

				} **/
				if (exception instanceof BizException) {
					logger.info("申请流水号 : 【" + applySeq + "】处理该插槽的业务异常开始，更新该插槽状态为失败， 更新网贷任务队列中队列任务状态为执行失败和更新 第三方额度调整申请表中调整状态为拒绝");
					// 更新该插槽状态为失败
					methodSoltstate.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.FAILURE.getCode());
					nlsProcessBizVo.setHangTimes(hangTimes);
					nlsProcessBizVo.setQueueTaskState(QueueTaskEnums.QueueTaskStateEnum.FAILURE.getCode());
					/** 设置插槽结果信息 **/
					lmtAdjustThirdVo.setSoltResultDetails(JSONObject.toJSONString(nlsProcessBizVo));
					
					/** 设置调整状态信息为失败 **/
					lmtAdjustThirdVo.setAdjStatus(DxmEnum.LoanAdjustSts.REJECT.getVALUE());
					// 通知调额申请失败
					this.noticeLoanAdjLmt(nlsProcessBizVo, lmtAdjustThirdVo);
					logger.info("申请流水号 : 【" + applySeq + "】处理该插槽的业务异常结束");
				} else {
					logger.info("申请流水号 : 【" + applySeq + "处理该插槽的其它异常开始，更新该插槽状态为失败， 更新网贷任务队列中队列任务状态为执行失败和更新 第三方额度调整申请表中调整状态为拒绝");
					// 更新该插槽状态为失败
					methodSoltstate.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.FAILURE.getCode());
					nlsProcessBizVo.setHangTimes(hangTimes);
					nlsProcessBizVo.setQueueTaskState(QueueTaskEnums.QueueTaskStateEnum.FAILURE.getCode());
					/** 设置插槽结果信息 **/
					lmtAdjustThirdVo.setSoltResultDetails(JSONObject.toJSONString(nlsProcessBizVo));

					/** 设置调整状态信息为失败 **/
					lmtAdjustThirdVo.setAdjStatus(DxmEnum.LoanAdjustSts.REJECT.getVALUE());
					// 通知调额申请失败
					this.noticeLoanAdjLmt(nlsProcessBizVo, lmtAdjustThirdVo);
					logger.info("申请流水号 : 【" + applySeq + "处理该插槽的其它异常结束");
				}
				/** 设置结果码 **/
				lmtAdjustThirdVo.setRspCode(nlsProcessBizVo.getRefuseMsg());
				/** 设置结果信息 **/
				lmtAdjustThirdVo.setRspMsg(DxmEnum.DxmRefuseMsg.getValue(nlsProcessBizVo.getRefuseMsg()));
				/** 设置结果信息 **/
				lmtAdjustThirdVo.setRejectReason(exception.getMessage());
				/** 最近修改时间 **/
				nlsProcessBizVo.setLastModifyTime(DateUtility.getCurrAppDateTimeString());
				/** 最近修改时间 **/
				lmtAdjustThirdVo.setLastUpdateTime(DateUtility.getCurrAppDateTimeString());
				nlsProcessBizService.updateByPk(nlsProcessBizVo);
				lmtAdjustThirdService.updateByPk(lmtAdjustThirdVo);

				throw exception;
			} else {
				logger.info("申请流水号 : 【" + applySeq + "该插槽的无异常开始，更新该插槽状态为成功，此处队列任务表和进件申请表的数据需要等插槽全部执行完成后才更新");
				// 更新该插槽状态为成功
				methodSoltstate.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.SUCCESS.getCode());
				nlsProcessBizVo.setHangTimes(QueueTaskEnums.QueueHangTimesEnum.INITIALIZE.getTimes());
				// 此处队列任务表和进件申请表的数据需要等插槽全部执行完成后才更新
				logger.info("申请流水号 : 【" + applySeq + "该插槽的无异常结束");
			}
			// 最近修改时间
			nlsProcessBizVo.setLastModifyTime(DateUtility.getCurrAppDateTimeString());
			nlsProcessBizService.updateByPk(nlsProcessBizVo);
		} catch (HangException e) {
			logger.error("队列插槽服务中申请流水号:【" + applySeq + "】的处理挂起异常信息为：" + e.getMessage());
			throw new HangException();
		} catch (BizException e) {
			logger.error("队列插槽服务中申请流水号:【" + applySeq + "】的业务异常信息为：" + e.getMessage());
			throw new BizException();
		} catch (Exception e) {
			logger.error("队列插槽服务中申请流水号:【" + applySeq + "】的其他异常信息为：" + e.getMessage());
			throw new Exception();
		}

	}

	/**
	 * 供插槽实现的抽象方法
	 */
	protected abstract void doExecute(NlsProcessBizVO nlsProcessBizVo, LmtAdjustThirdVO lmtAdjustThirdVo) throws Exception;

	/**
	 * 通知调额失败
	 * @author  qiuyf
	 * @since 2019年3月12日 下午4:22:01
	 * @version 0.2 初始为0.1,后续变更则修改
	 */
	private void noticeLoanAdjLmt(NlsProcessBizVO nlsProcessBizVo, LmtAdjustThirdVO lmtAdjustThirdVo) throws Exception {
		try {
			logger.info("-------------------------- 调用额度调整申请结果通知接口，通知调额申请失败开始..........................");
			ReqLoanAdjLmtNoticeBean reqLmtBean = new ReqLoanAdjLmtNoticeBean();
			/* 产品代码  */
			reqLmtBean.setProdCd(SDicMappingClient.getExternalSysDicVal(SDicClientConstant.SYSID_IOP, SDicClientConstant.PRD_CODE, lmtAdjustThirdVo.getPrdId()));
			/* 返回代码  */
			reqLmtBean.setRetCd(DxmEnum.RetCd.SUCCESS.getValue());
			/* 交易模式为“异步” */
			reqLmtBean.setTxnMd(DxmEnum.TxnModel.SYNC.getValue());
			/* 请求标志  = 请求方需要保证永远唯一,响应方据此判断是否重复请求 */
			reqLmtBean.setRepeatRqsBss(lmtAdjustThirdVo.getBdReqSn());
			/* 版本号 */
			reqLmtBean.setVerNo(lmtAdjustThirdVo.getVerNo());
			/* 请求时间戳*/
			reqLmtBean.setRqsTmstmp(TimeUtil.getTimeStampByPattern("yyyyMMddHHmmssSSS"));
			/* 会话ID */
			reqLmtBean.setSessionId(lmtAdjustThirdVo.getAdjSeq());
			/* 通知类型 */
			reqLmtBean.setAdvType(lmtAdjustThirdVo.getAdvType()+"Callback");
			
			/** 业务数据组装  start*/
			ReqLoanAdjBizmessage reqBizmessage = new ReqLoanAdjBizmessage();

			String instRsnCd = nlsProcessBizVo.getRefuseMsg();
			if(StringUtil.isNullorBank(instRsnCd)){
				instRsnCd = EsbBizEnums.RetCodeEnum.FAILURE.getVALUE();
			}

			reqBizmessage.setInstRsnCd(instRsnCd);
			reqBizmessage.setInstRsnCdRmk(nlsProcessBizVo.getExceptionMsg().getMessage());
			/* 机构策略码 */
			reqBizmessage.setInstPolcyCd(DxmEnum.OrgStrategyCode.REJECT_90000.getVALUE()); 
			/* 确认调额后金额 */
			reqBizmessage.setCfrmAdjtAmt(lmtAdjustThirdVo.getAdjResultLmt().toString());
			/** 业务数据组装 end */ 
			reqLmtBean.setBizmessage(reqBizmessage);
			
			RespLoanAdjLmtNoticeBean respBean = loanAdjLmtNoticeService.loanAdjLmtNotice(reqLmtBean);
			
			logger.info("************* 调用额度调整申请结果通知接口返回信息为："+JSON.toJSONString(respBean));
		} catch (Exception e) {
			logger.error("*********** 调用额度调整申请结果通知接口通知调额申请失败发生异常，异常信息为："+e.getMessage());
		} finally {
			logger.info("-------------------------- 调用额度调整申请结果通知接口，通知调额申请失败结束--------------------------");
		}

	}

	@Autowired
	@Qualifier("nlsQueueTaskService")
	private NlsQueueTaskService nlsQueueTaskService;
	@Autowired
	@Qualifier("nlsProcessBizService")
	private NlsProcessBizService nlsProcessBizService;
	@Autowired
	@Qualifier("lmtAdjustThirdService")
	private LmtAdjustThirdService lmtAdjustThirdService;
	@Autowired
	@Qualifier("loanAdjLmtNoticeService")
	private LoanAdjLmtNoticeService loanAdjLmtNoticeService;
	@Autowired
	@Qualifier("producerService")
	private MQProducerMessageClient producerService;
}
